using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Admin.NET.Application.Entity.Business;
using Admin.NET.Application.EventBus;
using Admin.NET.Core;
using Furion;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Events;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
using SqlSugar;

namespace Admin.NET.Application.Service.Mp;

/// <summary>
/// 微信支付
/// </summary>
[ApiDescriptionSettings(new string[] { "业务应用" }, Order = 100)]
public class MpPayService : IDynamicApiController, ITransient, IPrivateDependency
{
	private readonly UserManager _userManager;

	private readonly SqlSugarRepository<WxPayProvider> _rep;

	private readonly SqlSugarRepository<RoomBalanceRecord> _balanceRep;

	private readonly RoomService _roomService;

	public MpPayService(UserManager userManager, SqlSugarRepository<WxPayProvider> rep, SqlSugarRepository<RoomBalanceRecord> balanceRep, RoomService roomService)
	{
		_userManager = userManager;
		_rep = rep;
		_balanceRep = balanceRep;
		_roomService = roomService;
	}

	/// <summary>
	/// 创建订单编号
	/// </summary>
	/// <returns></returns>
	private string CreateOrderNo()
	{
		Random r = new Random();
		int r1 = r.Next(1, 9) * 10;
		int r2 = r.Next(1, 9) * 10;
		DateTime tmpTime = DateTime.Now;
		string now = tmpTime.ToString("yyMMddHHmmss") + "99" + tmpTime.Millisecond;
		return now + Convert.ToString(r1) + Convert.ToString(r2);
	}

	private async Task<WxPayProvider> GetProvider()
	{
		WxPayProvider data = await _rep.AsQueryable().FirstAsync();
		if (data == null)
		{
			throw Oops.Bah("该平台暂未开通微信支付，请线下缴费");
		}
		return data;
	}

	public WechatTenpayClient CreateWechatClient(WxPayProvider baseOptions)
	{
		WechatTenpayClientOptions tenpayClientOptions = new WechatTenpayClientOptions
		{
			MerchantId = baseOptions.MerchantId,
			MerchantV3Secret = baseOptions.MerchantV3Secret,
			MerchantCertificateSerialNumber = baseOptions.MerchantCertificateSerialNumber,
			MerchantCertificatePrivateKey = baseOptions.MerchantCertificatePrivateKey,
			PlatformCertificateManager = new InMemoryCertificateManager()
		};
		return new WechatTenpayClient(tenpayClientOptions);
	}

	private async Task<WxPaySub> GetSubOptions()
	{
		WxPaySub data = await (from u in _rep.Context.Queryable<WxPaySub>()
			where u.SubTenantId == _userManager.TenantId
			select u).FirstAsync();
		if (data == null)
		{
			throw Oops.Bah("暂未开通微信支付，请线下缴费");
		}
		return data;
	}

	[HttpPost("/mp/pay/createorder")]
	public async Task<dynamic> CreateOrder([FromBody] CreateOrderInput input)
	{
		WxPayProvider baseOptions = await GetProvider();
		WxPaySub subOptions = await GetSubOptions();
		string orderNo = CreateOrderNo();
		Room room = await (from w in _rep.Context.Queryable<Room>()
			where w.Id == input.RoomId
			select w).FirstAsync();
		if (room == null)
		{
			throw Oops.Bah("该房间不存在，无法充值缴费");
		}
		CreatePayPartnerTransactionJsapiRequest request = new CreatePayPartnerTransactionJsapiRequest
		{
			OutTradeNumber = orderNo,
			AppId = baseOptions.AppId,
			MerchantId = baseOptions.MerchantId,
			SubAppId = subOptions.SubAppId,
			SubMerchantId = subOptions.SubMerchantId,
			Description = input.Description,
			ExpireTime = DateTimeOffset.Now.AddMinutes(10.0),
			NotifyUrl = baseOptions.WechatPayUrl,
			Amount = new CreatePayPartnerTransactionJsapiRequest.Types.Amount
			{
				Total = input.Total
			},
			Payer = new CreatePayPartnerTransactionJsapiRequest.Types.Payer
			{
				SubOpenId = input.OpenId
			}
		};
		WechatTenpayClient WechatTenpayClient = CreateWechatClient(baseOptions);
		CreatePayPartnerTransactionJsapiResponse response = await WechatTenpayClient.ExecuteCreatePayPartnerTransactionJsapiAsync(request);
		if (!response.IsSuccessful())
		{
			throw Oops.Oh(response.ErrorMessage);
		}
		PaymentRecord wechatPay = new PaymentRecord
		{
			AppId = baseOptions.AppId,
			MerchantId = baseOptions.MerchantId,
			SubAppId = subOptions.SubAppId,
			SubMerchantId = subOptions.SubMerchantId,
			OutTradeNo = request.OutTradeNumber,
			Remark = input.Description,
			Amount = (decimal)((double)input.Total / 100.0),
			Rate = (decimal)((double)input.Rate / 100.0),
			OpenId = input.OpenId,
			Body = input.Description,
			IsPay = 0,
			PaymentName = "房间缴费",
			Subject = "房间缴费",
			PayType = PayTypeEnum.微信,
			BeforeAmount = room.Balance,
			BuildingId = room.BuildingId,
			OwnerId = room.OwnerId,
			RoomId = room.Id,
			CreateTime = DateTime.Now,
			CreateUserName = _userManager.RealName
		};
		if (input.IsCustomBill.GetValueOrDefault() == 1)
		{
			if (input.Bills == null || input.Bills.Count == 0)
			{
				throw Oops.Bah("无效账单，请重新进入页面");
			}
			List<long> billtypes = input.Bills.Select((CustomBill s) => s.BillType).ToList();
			List<BillType> types = await (from w in _rep.Context.Queryable<BillType>()
				where billtypes.Contains(w.Id)
				select w).ToListAsync();
			wechatPay.IsCustomBill = input.IsCustomBill;
			wechatPay.Bills = input.Bills.Select(delegate(CustomBill s)
			{
				BillType billType = types.Where((BillType w) => w.Id == s.BillType).FirstOrDefault();
				return new CustomBillRecord
				{
					BillType = s.BillType,
					Fee = s.Fee,
					Name = billType?.Name,
					IsSupportedInvoicing = billType?.IsSupportedInvoicing
				};
			}).ToList();
			wechatPay.CustomRoomBillId = input.CustomRoomBillId;
			wechatPay.PaymentName = input.PaymentName;
			wechatPay.Subject = input.PaymentName;
		}
		_rep.Context.Insertable(wechatPay).ExecuteCommand();
		IDictionary<string, string> paramMap = WechatTenpayClient.GenerateParametersForJsapiPayRequest(request.SubAppId, response.PrepayId);
		return new
		{
			orderInfo = paramMap,
			OutTradeNumber = request.OutTradeNumber,
			Payee = subOptions.SubName,
			PayTime = wechatPay.CreateTime
		};
	}

	/// <summary>
	/// 微信支付成功回调(服务商模式)
	/// </summary>
	/// <returns></returns>
	[HttpPost("/mp/pay/payPartnerCallback")]
	[AllowAnonymous]
	[NonUnify]
	public async Task WechatPayPartnerCallBack()
	{
		using MemoryStream ms = new MemoryStream();
		await App.HttpContext.Request.Body.CopyToAsync(ms);
		byte[] b = ms.ToArray();
		string callbackJson = Encoding.UTF8.GetString(b);
		WechatTenpayClient WechatTenpayClient = CreateWechatClient(await GetProvider());
		WechatTenpayEvent callbackModel = WechatTenpayClient.DeserializeEvent(callbackJson);
		if (!"TRANSACTION.SUCCESS".Equals(callbackModel.EventType))
		{
			return;
		}
		PartnerTransactionResource callbackResource = WechatTenpayClient.DecryptEventResource<PartnerTransactionResource>(callbackModel);
		long roomId = 0L;
		try
		{
			await _rep.Context.Ado.BeginTranAsync();
			await _balanceRep.Context.Ado.BeginTranAsync();
			PaymentRecord wechatPay = await (from u in _rep.Context.Queryable<PaymentRecord>()
				where u.OutTradeNo == callbackResource.OutTradeNumber && u.MerchantId == callbackResource.MerchantId
				select u).FirstAsync();
			if (wechatPay == null || wechatPay.IsPay != 0)
			{
				return;
			}
			roomId = wechatPay.RoomId.Value;
			wechatPay.TradeNo = callbackResource.TransactionId;
			wechatPay.TradeType = callbackResource.TradeType;
			wechatPay.TradeStatus = callbackResource.TradeState;
			wechatPay.TradeStateDescription = callbackResource.TradeStateDescription;
			wechatPay.BankType = callbackResource.BankType;
			wechatPay.BuyerPayAmount = ((double?)callbackResource.Amount.PayerTotal / 100.0).ToString();
			wechatPay.SuccessTime = callbackResource.SuccessTime;
			wechatPay.NotifyTime = DateTime.Now;
			wechatPay.IsPay = 1;
			if (!string.IsNullOrWhiteSpace(wechatPay.BuyerPayAmount))
			{
				wechatPay.ReceiptAmount = decimal.Parse(wechatPay.BuyerPayAmount);
			}
			await _rep.Context.Updateable(wechatPay).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
			decimal amount = wechatPay.Amount - wechatPay.Rate;
			if (wechatPay.IsCustomBill.GetValueOrDefault() == 1)
			{
				IUpdateable<CustomRoomBill> tmp = _rep.Context.Updateable<CustomRoomBill>();
				tmp = tmp.SetColumns((CustomRoomBill s) => s.Status == 2);
				await tmp.Where((CustomRoomBill s) => (long?)s.Id == wechatPay.CustomRoomBillId).ExecuteCommandAsync();
			}
			else
			{
				IUpdateable<Room> tmp = _rep.Context.Updateable<Room>();
				tmp = tmp.SetColumns((Room s) => new Room
				{
					Balance = s.Balance + amount
				});
				await tmp.Where((Room s) => (long?)s.Id == wechatPay.RoomId).ExecuteCommandAsync();
				await _roomService.AddRoomBalanceRecord(_balanceRep.Context, wechatPay.RoomId.Value, wechatPay.Id, 0, wechatPay.Amount, wechatPay.TenantId.Value, "微信充值");
			}
			try
			{
				Room roomInfo = await (from w in _rep.Context.Queryable<Room>()
					where w.Id == roomId && w.TenantId == wechatPay.TenantId
					select w).FirstAsync();
				if (roomInfo != null && roomInfo.OwnerId.HasValue)
				{
					Owner owner = await (from w in _rep.Context.Queryable<Owner>()
						where (long?)w.Id == roomInfo.OwnerId && w.TenantId == wechatPay.TenantId
						select w).FirstAsync();
					if (owner != null)
					{
						BillNoticePayload billNoticePayload = new BillNoticePayload
						{
							Body = $"您的房间：{roomInfo.Name}缴费成功，金额{wechatPay.Amount}元，到账{amount}元",
							Subject = "缴费成功提醒",
							Owners = new List<Owner> { owner },
							RoomId = roomInfo.Id,
							RoomName = roomInfo.Name,
							OrderNo = wechatPay.OutTradeNo,
							Amount = wechatPay.Amount.ToString(),
							Path = "/userPage/roomDetail/roomDetail?roomId=" + roomInfo.Id
						};
						EventPayload payload = new EventPayload
						{
							Data = JsonConvert.SerializeObject(billNoticePayload),
							TenantId = roomInfo.TenantId.Value
						};
						for (int i = 0; i < 4; i++)
						{
							MessageCenter.PublishAsync($"{i}:SendPaymentNotice", payload).Wait();
						}
					}
				}
			}
			catch (Exception)
			{
			}
			await _rep.Context.Ado.CommitTranAsync();
			await _balanceRep.Context.Ado.CommitTranAsync();
			try
			{
				EventPayload payload2 = new EventPayload
				{
					Data = roomId.ToString(),
					TenantId = wechatPay.TenantId.Value
				};
				await MessageCenter.PublishAsync("SwitchOnGate", payload2);
			}
			catch (Exception)
			{
			}
		}
		catch (Exception ex)
		{
			await _rep.Context.Ado.RollbackTranAsync();
			await _balanceRep.Context.Ado.RollbackTranAsync();
			throw ex;
		}
	}
}
